package com.rankeiot.core.util;


import lombok.SneakyThrows;
import org.springframework.util.StopWatch;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

@SuppressWarnings("AlibabaConstantFieldShouldBeUpperCase")
public class RsaUtil {
    private static final String algorithm = "RSA";
    private static final int maxSize = 1024;

    /**
     * 从字符串中加载公钥
     *
     * @param keyString : 公钥字符串
     * @return : 公钥
     * @throws Exception
     */
    public static PublicKey loadPublicKeyFromString(String keyString) throws Exception {

        // 进行Base64解码
        byte[] decode = Base64.getDecoder().decode(keyString);
        return loadPublicKeyFromData(decode);
    }

    public static PublicKey loadPublicKeyFromData(byte[] data) throws Exception {

        // 获取密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 构建密钥规范
        X509EncodedKeySpec keyspec = new X509EncodedKeySpec(data);
        // 获取公钥
        return keyFactory.generatePublic(keyspec);

    }

    /**
     * 从字符串中加载私钥
     *
     * @param keyString : 私钥字符串
     * @return : 私钥
     * @throws Exception
     */
    private static PrivateKey loadPrivateKeyFromString(String keyString) throws Exception {
        // 进行Base64解码
        byte[] decode = Base64.getDecoder().decode(keyString);//Base64.decode(keyString);
        // 获取密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 构建密钥规范
        PKCS8EncodedKeySpec keyspec = new PKCS8EncodedKeySpec(decode);
        // 生成私钥
        return keyFactory.generatePrivate(keyspec);
    }

    /**
     * 使用密钥加密数据
     *
     * @param input : 原文
     * @param key   : 密钥
     * @return : 密文
     * @throws Exception
     */
    public static String encrypt(String input, Key key) throws Exception {
        int maxEncryptSize = input.length();
        // 获取Cipher对象
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化模式(加密)和密钥
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 将原文转为byte数组
        byte[] data = input.getBytes();
        // 总数据长度
        int total = data.length;
        // 输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        decodeByte(cipher, data, total, baos);
        // 对密文进行Base64编码
        return Base64.getEncoder().encodeToString(baos.toByteArray());

    }
    public static String decrypt(byte[] data, Key key) throws Exception {
        // 获取Cipher对象
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化模式(解密)和密钥
        cipher.init(Cipher.DECRYPT_MODE, key);
        // 总数据长度
        int total = data.length;
        // 输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        decodeByte(cipher, data, total, baos);
        // 输出原文
        return baos.toString();
    }
    /**
     * 解密数据
     *
     * @param encrypted : 密文
     * @param key       : 密钥
     * @return : 原文
     * @throws Exception
     */
    public static String decrypt(String encrypted, Key key) throws Exception {
        // 由于密文进行了Base64编码, 在这里需要进行解码
        byte[] data = Base64.getDecoder().decode(encrypted);//Base64.decode(encrypted);
        return decrypt(data,key);
    }

    /**
     * 分段处理数据
     *
     * @param cipher : Cipher对象
     * @param data   : 要处理的byte数组
     * @param total  : 总数据长度
     * @param baos   : 输出流
     * @throws Exception
     */
    private static void decodeByte(Cipher cipher, byte[] data, int total, ByteArrayOutputStream baos) throws Exception {
        // 偏移量
        int offset = 0;
        // 缓冲区
        byte[] buffer;
        // 如果数据没有处理完, 就一直继续
        while (total - offset > 0) {
            // 如果剩余的数据 >= 最大处理能力, 就按照最大处理能力来加密数据
            if (total - offset >= maxSize) {
                // 加密数据
                buffer = cipher.doFinal(data, offset, maxSize);
                // 偏移量向右侧偏移最大数据能力个
                offset += maxSize;
            } else {
                // 如果剩余的数据 < 最大处理能力, 就按照剩余的个数来加密数据
                buffer = cipher.doFinal(data, offset, total - offset);
                // 偏移量设置为总数据长度, 这样可以跳出循环
                offset = total;
            }
            // 向输出流写入数据
            baos.write(buffer);
        }
    }
    /**
     * 直接生成公钥、私钥对象
     *
     * @param modulus
     *
     * @throws NoSuchAlgorithmException
     *
     */
    public static KeyPair getRSAKeyObject(int modulus) {

        List<Key> keyList = new ArrayList<>(2);
        // 创建RSA密钥生成器
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 设置密钥的大小，此处是RSA算法的模长 = 最大加密数据的大小
        keyPairGen.initialize(modulus);
        KeyPair keyPair = keyPairGen.generateKeyPair();

        return keyPair;
    }

    @SneakyThrows
    private static byte[] md5(String value){
        byte[] btInput = value.getBytes("UTF-8");
        MessageDigest mdInst = MessageDigest.getInstance("MD5");
        mdInst.update(btInput);
        return mdInst.digest();
    }
    @SneakyThrows
    public static String AESEncrypt(String value, String key){
        byte[] keyBytes =md5(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // 根据key的字节内容，"恢复"秘钥对象
        SecretKey keySec = new SecretKeySpec(keyBytes,"AES");
        // 初始化秘钥:设置加密模式ENCRYPT_
        cipher.init(Cipher.ENCRYPT_MODE, keySec,new IvParameterSpec(keyBytes));
        // 根据原始内容(字节),进行加密
        return StringUtil.hex(cipher.doFinal(value.getBytes()));
    }


    @SneakyThrows
    public static String AESDecrypt(String value, String key){
        byte[] keyBytes =md5(key);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // 根据key的字节内容，"恢复"秘钥对象
        SecretKey keySec = new SecretKeySpec(keyBytes,"AES");
        // 初始化秘钥:设置解密模式DECRYPT_MODE
        cipher.init(Cipher.DECRYPT_MODE, keySec,new IvParameterSpec(keyBytes));
        // 根据原始内容(字节),进行加密
        return new String(cipher.doFinal(StringUtil.hexToBytes(value)));
    }
    /**
    @SneakyThrows
    public static String aesCtrEncrypt(String value,String key){
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
        byte[] btInput = key.getBytes("UTF-8");
        MessageDigest mdInst = MessageDigest.getInstance("MD5");
        mdInst.update(btInput);
        byte[] encKey = mdInst.digest();
        SecretKeySpec keySpec = new SecretKeySpec(encKey, "AES");

        cipher.init(Cipher.ENCRYPT_MODE, keySpec,new IvParameterSpec(encKey));
        return StringUtil.hex(cipher.doFinal(value.getBytes()));
    }
 **/
//    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
//       String enc= AESEncrypt("aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzzz","bb");
//
//        System.out.println(enc);
//
//        System.out.println(AESDecrypt(enc,"bb"));
//
//     }
}
